package com.tiancheng.trade.payment.task.lkl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.lkl.laop.sdk.request.V3SacsSeparateRequest;
import com.lkl.laop.sdk.request.model.V3SacsSeparateRecvDatas;
import com.tiancheng.commom.pay.lkl.LklPay;
import com.tiancheng.commom.pay.lkl.LklPayProperties;
import com.tiancheng.commom.pay.lkl.res.LklBaseRes;
import com.tiancheng.commom.pay.lkl.res.LklSeparateRes;
import com.tiancheng.trade.commom.core.enums.PaymentChannelEnum;
import com.tiancheng.trade.commom.core.enums.SettleStatusEnum;
import com.tiancheng.trade.commom.core.exception.Asserts;
import com.tiancheng.trade.commom.core.exception.BusinessException;
import com.tiancheng.trade.commom.core.exception.error.BusinessErrorInfoEnum;
import com.tiancheng.trade.commom.core.utils.DateTimeUtil;
import com.tiancheng.trade.commom.core.utils.JsonUtil;
import com.tiancheng.trade.payment.bo.PaymentFlowDetailSettleBO;
import com.tiancheng.trade.payment.bo.PaymentFlowDetailSettleDetailBO;
import com.tiancheng.trade.payment.bo.SettleFlowDetailMerchantBO;
import com.tiancheng.trade.payment.channel.lkl.LklPayChannel;
import com.tiancheng.trade.payment.channel.lkl.LklPayConfig;
import com.tiancheng.trade.payment.channel.umsshop.UmsShopAllConfig;
import com.tiancheng.trade.payment.channel.umsshop.UmsShopProperties;
import com.tiancheng.trade.payment.channel.umsshop.UmsShopSettleClient;
import com.tiancheng.trade.payment.channel.umsshop.request.UmsShopProcessWithdrawReq;
import com.tiancheng.trade.payment.channel.umsshop.request.UmsShopWithdrawStatusQueryReq;
import com.tiancheng.trade.payment.channel.umsshop.response.UmsShopProcessWithdrawRes;
import com.tiancheng.trade.payment.channel.umsshop.response.UmsShopWithdrawMerchantBalanceQueryRes;
import com.tiancheng.trade.payment.channel.umsshop.response.UmsShopWithdrawStatusQueryRes;
import com.tiancheng.trade.payment.convert.SettleConvert;
import com.tiancheng.trade.payment.enums.WithdrawalStatusEnum;
import com.tiancheng.trade.payment.model.*;
import com.tiancheng.trade.payment.service.*;
import com.tiancheng.trade.payment.util.helper.PaymentIdHelper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionTemplate;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 拉卡拉结算任务
 */
@Component
@Slf4j
public class LklSettleTask {

    @Resource
    private RedissonClient redissonClient;
    @Resource
    private PaymentFlowDetailSettleService paymentFlowDetailSettleService;
    @Resource
    private IPaymentFlowDetailSettleDetailService paymentFlowDetailSettleDetailService;
    @Resource
    private IRefundSettleDetailService refundSettleDetailService;
    @Resource
    private IRefundSettleService refundSettleService;
    @Resource
    private LklPayConfig lklPayConfig;
    @Resource
    private IWithdrawalService withdrawalService;
    @Resource
    private PaymentOrderService paymentOrderService;

    public static final String CHANNEL_NAME = PaymentChannelEnum.lkl_pay.getDesc();


    //@Scheduled(cron = "0 0 15 * * ?")
    public void doSettle() {
        log.info(CHANNEL_NAME + "-> 自动清分任务开始执行：{}", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        // 同时刻不允许有多个任务处理相同数据
        RLock lock = redissonClient.getLock("LKL_PAY_SETTLE_LOCK");
        boolean isLock;
        try {
            isLock = lock.tryLock(30L, TimeUnit.MINUTES);
            if (!isLock) {
                log.error(CHANNEL_NAME + "-> 自动清分任务执行结果：锁失败！");
                return;
            }
            // 1、查询等待清分商户信息
            final List<SettleFlowDetailMerchantBO> detailMerchantBOS = paymentFlowDetailSettleService.distinctSettleMerchant(LocalDate.now(), PaymentChannelEnum.lkl_pay.getCode());
            if (detailMerchantBOS.isEmpty()) {
                log.info(CHANNEL_NAME + "-> 自动清分任务执行结果：无应清分数据！");
                return;
            }

            // 2、查询等待提现的明细项
            // 按商户聚合-支付结算明细
            List<PaymentFlowDetailSettleBO> allSettles = queryPayOrderSettleDetails();
            if (CollectionUtils.isEmpty(allSettles)) {
                log.info(CHANNEL_NAME + "-> 当日无提现订单！");
                return;
            }

            List<PaymentFlowDetailSettleBO> successed = new ArrayList<>();
            List<Withdrawal> withdrawals = new ArrayList<>();
            // 3、遍历结算订单
            for (PaymentFlowDetailSettleBO settleBO : allSettles) {
                if(CollectionUtils.isEmpty(settleBO.getDetails())){
                    log.error("待结算订单分账明细为空！");
                    continue;
                }
                String withdrawalFlowId = PaymentIdHelper.getWithdrawalFlowId(settleBO.getSubFlowId());

                LklPayProperties lklPayProperties = initLklPayConfig(settleBO.getOrgPaymentChannelId());
                V3SacsSeparateRequest settleItem = new V3SacsSeparateRequest();
                settleItem.setMerchantNo(lklPayProperties.getMerchantNo());
                settleItem.setLogNo(settleBO.getSettleLogNo());
                settleItem.setLogDate(DateTimeUtil.format(LocalDateTime.now(),DateTimeUtil.yyyyMMdd));
                settleItem.setOutSeparateNo(PaymentIdHelper.getWithdrawalFlowId(settleBO.getFlowId()));
                settleItem.setNotifyUrl(lklPayProperties.getNotifyUrl());
                //分账接收数据对象
                List<V3SacsSeparateRecvDatas> recvDatas = new ArrayList<>();

                for (PaymentFlowDetailSettleDetailBO detail : settleBO.getDetails()) {
                    V3SacsSeparateRecvDatas recvData = new V3SacsSeparateRecvDatas();
                    if(detail.getSubMerchantType().equals(1)){
                        recvData.setRecvMerchantNo(detail.getSettleMerchantId());
                    }else{
                        recvData.setRecvNo(detail.getSettleMerchantId());
                    }
                    recvData.setSeparateValue(detail.getAmount().toString());
                    recvDatas.add(recvData);
                }
                settleItem.setTotalAmt(String.valueOf(settleBO.getDetails().stream().mapToInt(PaymentFlowDetailSettleDetailBO::getAmount).sum()));
                settleItem.setLklOrgNo("1");
                settleItem.setCalType("0");
                settleItem.setRecvDatas(recvDatas);
                // 拉卡拉分账返回
                LklBaseRes<LklSeparateRes> lklSeparateResLklBaseRes = new LklPay(lklPayProperties).orderSettle(settleItem);
                if(lklSeparateResLklBaseRes.isOK()){
                    // 拉卡拉分账单号
                    String separateNo = lklSeparateResLklBaseRes.getRespData().getSeparateNo();
                    settleBO.setChannelSettleNo(separateNo);
                    // 保存提现记录
                    Withdrawal withdrawal = new Withdrawal();
                    withdrawal.setWithdrawalId(withdrawalFlowId);
                    withdrawal.setSsn(separateNo);
                    PaymentFlowDetailSettleDetailBO tradeMerchant = tradeMerchant(settleBO.getDetails());
                    withdrawal.setMerchantRegId(tradeMerchant.getMerchantRegId());
                    withdrawal.setMerchantCode(tradeMerchant.getMerchantCode());
                    withdrawal.setMerchantName(tradeMerchant.getMerchantName());
                    withdrawal.setSubMerchantId(tradeMerchant.getSubMerchantId());
                    withdrawal.setEnterpriseCode(tradeMerchant.getSettleMerchantId());
                    withdrawal.setAmount(Integer.parseInt(settleItem.getTotalAmt()));
                    withdrawal.setFee(0);
                    withdrawal.setWithdrawalStatus(WithdrawalStatusEnum.ING.getCode());
                    withdrawal.setOrgPaymentChannelId(settleBO.getOrgPaymentChannelId());
                    withdrawal.setPaymentChannel(settleBO.getPaymentChannel());
                    withdrawal.setCompleteTime(LocalDateTime.now());
                    withdrawal.setUpdateTime(LocalDateTime.now());
                    withdrawal.setCreateTime(LocalDateTime.now());

                    settleBO.setSettleStatus(SettleStatusEnum.ING.getCode());
                    settleBO.getDetails().forEach(sb->sb.setWithdrawalId(withdrawalFlowId));
                    withdrawals.add(withdrawal);
                    successed.add(settleBO);
                }

            }

            // TODO 结算状态
            List<PaymentFlowDetailSettleDetail> pfdsds = new ArrayList<>();
            Set<PaymentFlowDetailSettle> updatePfds = successed.stream().map(d -> {
                PaymentFlowDetailSettle pfds = new PaymentFlowDetailSettle();
                pfds.setId(d.getId());
                pfds.setSettleStatus(d.getSettleStatus());
                pfds.setChannelSettleNo(d.getChannelSettleNo());

                for (PaymentFlowDetailSettleDetailBO detail : d.getDetails()) {
                    PaymentFlowDetailSettleDetail item = new PaymentFlowDetailSettleDetail();
                    item.setId(detail.getId());
                    item.setSettleStatus(pfds.getSettleStatus());
                    item.setWithdrawalId(detail.getWithdrawalId());
                    pfdsds.add(item);
                }
                return pfds;
            }).collect(Collectors.toSet());
            withdrawalService.saveBatch(withdrawals);
            paymentFlowDetailSettleService.updateBatchById(updatePfds);
            paymentFlowDetailSettleDetailService.updateBatchById(pfdsds);
        } catch (Exception e) {
            log.error(CHANNEL_NAME + "-> 提现自动任务执行失败：", e);
            throw new BusinessException(BusinessErrorInfoEnum.UMS_SETTLE_ERROR, null, CHANNEL_NAME + "-> 提现自动任务执行失败", e);
        } finally {
            if (lock.isLocked()) {
                lock.unlock();
            }
        }
    }

    private PaymentFlowDetailSettleDetailBO tradeMerchant(List<PaymentFlowDetailSettleDetailBO> details){
        return details.stream().filter(d->d.getSubMerchantType().equals(1)).findFirst().get();
    }

    private LklPayProperties initLklPayConfig(Long orgPaymentChannelId) {
        return lklPayConfig.getByOrgPayChannelId(orgPaymentChannelId);
    }

    private String getRemark(String merchantName) {
        String str = StringUtils.isNotEmpty(merchantName) ? merchantName + "分账所得" : "商户分账所得";
        return str.length() >= 20 ? str.substring(0, 19) : str;
    }


    /**
     * 汇总当前等待轧差的退款结算单
     */
    private Map<Long, List<RefundSettleDetail>> queryRefundOrderSettleDetails() {
        long size = 1000L; // 每批次查询起始id
        long current = 1;// 当前页
        Map<Long, List<RefundSettleDetail>> merchantRefundSettleDetailsMap = new HashMap<>();
        List<RefundSettle> list;
        do {
            // 3.1、抓取当日应清分子订单和滞留子订单，每次抓取1000条
            IPage<RefundSettle> refundSettleIPage = refundSettleService.listWaitSettles(LocalDate.now(), PaymentChannelEnum.lkl_pay.getCode(), current, size);
            list = refundSettleIPage.getRecords();
            if (CollectionUtils.isEmpty(list)) {
                log.info("未查询到待结算订单或待结算订单处理完毕！");
                break;
            }
            // 支付流水清分明细项
            List<RefundSettleDetail> refundSettleDetails = refundSettleDetailService.queryBySettleIds(list.stream().map(RefundSettle::getId).collect(Collectors.toSet()));
            current++;
            // 按商户分组所有的结算明细项
            if (CollectionUtils.isEmpty(refundSettleDetails)) {
                continue;
            }
            // 按商户分组所有的结算明细项
            Map<Long, List<RefundSettleDetail>> merchantSettles = refundSettleDetails.stream().collect(Collectors.groupingBy(RefundSettleDetail::getMerchantRegId));
            if (!merchantSettles.isEmpty()) {
                merchantSettles.forEach((merchantId, refundFlowDetailSettleDetails) -> {
                    if (merchantRefundSettleDetailsMap.containsKey(merchantId) && CollectionUtils.isNotEmpty(merchantRefundSettleDetailsMap.get(merchantId))) {
                        merchantRefundSettleDetailsMap.get(merchantId).addAll(refundFlowDetailSettleDetails);
                    } else {
                        merchantRefundSettleDetailsMap.put(merchantId, refundFlowDetailSettleDetails);
                    }
                });
            }
        } while (list.size() > 999);
        return merchantRefundSettleDetailsMap;
    }

    /**
     * 汇总当前等待提现的订单明细
     *
     */
    private List<PaymentFlowDetailSettleBO> queryPayOrderSettleDetails() {
        long size = 1000L; // 每批次查询起始id
        long current = 1;// 当前页
        List<PaymentFlowDetailSettleBO> allSettles = new ArrayList<>();
        List<PaymentFlowDetailSettle> list;
        do {
            // 3.1、抓取当日应清分子订单和滞留子订单，每次抓取1000条
            IPage<PaymentFlowDetailSettle> flowDetailSettleIPage = paymentFlowDetailSettleService.listWaitSettles(LocalDate.now(), PaymentChannelEnum.lkl_pay.getCode(), current, size);
            list = flowDetailSettleIPage.getRecords();
            if (CollectionUtils.isEmpty(list)) {
                log.info("未查询到待结算订单或待结算订单处理完毕！");
                break;
            }
            // 支付流水清分明细项
            List<PaymentFlowDetailSettleDetail> flowDetailSettleDetails = paymentFlowDetailSettleDetailService.queryWaitSettleDetailsBySettleIds(list.stream().map(PaymentFlowDetailSettle::getId).collect(Collectors.toSet()));
            current++;
            // 按商户分组所有的结算明细项
            if (CollectionUtils.isEmpty(flowDetailSettleDetails)) {
                continue;
            }
            Set<String> flowIds = list.stream().map(PaymentFlowDetailSettle::getFlowId).collect(Collectors.toSet());
            List<PaymentOrder> paymentOrders = paymentOrderService.list(new LambdaQueryWrapper<PaymentOrder>().in(PaymentOrder::getFlowId, flowIds));
            for (PaymentFlowDetailSettle record : flowDetailSettleIPage.getRecords()) {
                List<PaymentFlowDetailSettleDetail> pfdsds = flowDetailSettleDetails.stream().filter(fdsd -> fdsd.getSubFlowId().equals(record.getSubFlowId())).collect(Collectors.toList());
                if(CollectionUtils.isNotEmpty(pfdsds)){
                    PaymentFlowDetailSettleBO settleBO = SettleConvert.INSTANCE.convert2PaymentFlowDetailSettleBO(record, pfdsds);
                    PaymentOrder paymentOrder = paymentOrders.stream().filter(po -> po.getFlowId().equals(record.getFlowId())).findFirst().get();
                    settleBO.setSettleLogNo(paymentOrder.getTransactionId());
                    allSettles.add(settleBO);
                }
            }
        } while (list.size() > 999);
        return allSettles;
    }
}
